package com.tianji.learning.service.impl;


import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSearchDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.AssertUtils;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.utils.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Service;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningRecordService;

import java.time.Duration;
import java.util.List;

import static com.tianji.common.constants.Constant.DATA_FIELD_NAME_LATEST_LEARN_TIME;
import static com.tianji.learning.enums.SectionType.VIDEO;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author dayu
 * @since 2024-08-22
 */
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {
  private final ILearningLessonService lessonService;
  private final CourseClient courseClient;
  private final LearningRecordDelayTaskHandler taskHandler;
  @Override
  public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
    // 1.获取登录用户
    Long userId = UserContext.getUser();
    // 2.查询课表
    LearningLesson lesson = lessonService.queryByUserAndCourseId(userId, courseId);
    if (ObjectUtil.isEmpty(lesson)) {
      return null;
    }
    LearningLessonDTO dto = new LearningLessonDTO();
    dto.setId(lesson.getId());
    dto.setLatestSectionId(lesson.getLatestSectionId());
    List<LearningRecord> list = lambdaQuery().eq(LearningRecord::getLessonId, lesson.getId()).list();
    dto.setRecords(BeanUtils.copyList(list, LearningRecordDTO.class));
    return dto;
  }

  @Override
  public void addLearningRecord(LearningRecordFormDTO formDTO) {
    // 缓存到redis中 大key learning:record:课表id  小key是小节id ，小节id+进度+是否完成
    boolean b = formDTO.getSectionType() == VIDEO ? handleVideoRecord(formDTO) : handleExamRecord(formDTO);
    if(!b && formDTO.getDuration() == -1){   // 如果firstFinished为false，并且duration为-1证明本次是暂存进度至redis，无需处理课表数据，而是等待延时检测任务处理
      return;
    }
    // 1、查询课表数据（已学习小节数、课表ID）
    LearningLesson lesson = this.lessonService.getById(formDTO.getLessonId());
    AssertUtils.isNotNull(lesson, "课表数据不存在，无法更新数据");
    // 是否有新的小节完成
    boolean finished = false;
    if (b) {
      // 查询总小节数
      CourseFullInfoDTO searchInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
      // 判断是否全部学习完成
      finished = lesson.getLearnedSections() + 1 >= searchInfo.getSectionNum();
    }

    // 更新数据
    boolean updateResult = this.lessonService.lambdaUpdate()
            .set(LearningLesson::getLatestSectionId, formDTO.getSectionId())  //最近学习小节ID
            .set(LearningLesson::getLatestLearnTime, formDTO.getCommitTime()) //最近学习时间
            .set(b,LearningLesson::getLearnedSections,lesson.getLearnedSections() +1)
            //.setSql(b, "learned_sections = learned_sections + 1") // 如果该小节已学完，将已学习小节数 +1
            .set(lesson.getStatus().equals(LessonStatus.NOT_BEGIN), LearningLesson::getStatus, LessonStatus.LEARNING)
            .set(finished, LearningLesson::getStatus, LessonStatus.FINISHED)
            .eq(LearningLesson::getId, lesson.getId())
            .update();
    if (!updateResult) {
      throw new DbException("修改课表数据失败！");
    }

  }

  /**
   * 处理视频记录
   * 如果学习完成就传 true
   *
   * @param formDTO
   * @return
   */
  private boolean handleVideoRecord(LearningRecordFormDTO formDTO) {
    // 根据课程id 小节id 查询课表信息
    LearningRecord record = getOne(new QueryWrapper<LearningRecord>()
            .lambda()
            .eq(LearningRecord::getLessonId, formDTO.getLessonId())
            .eq(LearningRecord::getSectionId, formDTO.getSectionId()));
    // 判null 是否有学习记录
    if (ObjectUtil.isEmpty(record)) {
      LearningRecord newRecord = BeanUtils.copyBean(formDTO, LearningRecord.class);
      newRecord.setUserId(UserContext.getUser());

      // 修改学习记录
      boolean result = save(newRecord);
      if (!result) {
        throw new DbException("新增学习记录失败！");
      }
      return false;
    }
    // 判断是否是第一次学习
    boolean firstFinished = !record.getFinished() && (formDTO.getMoment()) << 1 >= formDTO.getDuration();
    // 4.2、如果是非第一次学完，将学习记录暂存至redis，并且提交一个延时检测任务
    if(!firstFinished){
      LearningRecord learningRecord = new LearningRecord();
      learningRecord.setLessonId(formDTO.getLessonId());
      learningRecord.setSectionId(formDTO.getSectionId());
      learningRecord.setId(record.getId());
      learningRecord.setMoment(formDTO.getMoment());
      learningRecord.setFinished(record.getFinished());
      taskHandler.addLearningRecordTask(learningRecord);
      formDTO.setDuration(-1);
      return false;
    }
    // 更新数据 本地数据库
    boolean updateResult = this.lambdaUpdate()
            .set(LearningRecord::getMoment, formDTO.getMoment())
            .set(firstFinished, LearningRecord::getFinished, true)
            .set(firstFinished, LearningRecord::getFinishTime, formDTO.getCommitTime())
            .eq(LearningRecord::getId, record.getId())
            .update();
    if (!updateResult) {
      throw new DbException("更新学习记录失败！");
    }
    // 清理redis缓存中的学习记录（目的是为了保证Redis双写一致性！）
    taskHandler.cleanRecordCache(formDTO.getLessonId(),formDTO.getSectionId());
    return true;
  }

  /**
   * 处理考试记录
   *
   * @param formDTO
   * @return
   */
  private boolean handleExamRecord(LearningRecordFormDTO formDTO) {
    // 新增学习记录
    LearningRecord record = BeanUtils.copyBean(formDTO, LearningRecord.class);
    record.setFinished(true);
    record.setFinishTime(formDTO.getCommitTime());
    boolean result = save(record);
    if (!result) {
      throw new DbException("新增考试记录失败！");
    }
    return true;
  }
}
